Saltar al contenido principal

8th Wall – Proyectos Inmersys

Fecha de creación del post: 18 de Noviembre de 2025
Autor: Abraham García

Guía Técnica


Introducción

En Inmersys, nuestras experiencias con 8th Wall están diseñadas para ofrecer contenidos 2D previos a experiencias 3D inmersivas, principalmente utilizados para:

  • Aviso de privacidad
  • Instrucciones
  • Branding previo
  • Pantallas iniciales / loading screens
  • Información contextual del proyecto

Estas pantallas 2D funcionan como la capa previa antes de mostrar el entorno 3D completo de AR. Su objetivo es reforzar el branding, preparar al usuario y permitir cargas progresivas antes de mostrar los modelos o interacciones principales.


Estructura base de un proyecto

Un proyecto mínimo en 8th Wall + Inmersys está compuesto por tres archivos principales:

1. head.html

Contiene:

  • Scripts principales de 8th Wall
  • Scripts de A-Frame
  • Scripts personalizados
  • Configuración global (metas, estilos, libraries externas, etc.)

Ejemplos de scripts comunes:

  • xrextras.js
  • aframe.min.js
  • three.js
  • Assets loaders (GLTF, HDRI, etc.)

2. app.js

Es el archivo JavaScript principal, donde:

  • Se registran componentes personalizados de A-Frame
  • Se controlan eventos, lógica de interacción y flujos dinámicos
  • Se crean entidades o se manipulan elementos del DOM
  • Se contienen funciones comunes del proyecto (loaders, audio, UX, etc.)

3. body.html

Contiene:

  • Estructura HTML del proyecto
  • Elementos visuales (pantallas 2D)
  • Escena de A-Frame (<a-scene>)
  • Componentes de XRExtras
  • Canvas principal
  • Elementos UI/UX del flujo

Tecnologías utilizadas en los proyectos Inmersys

Frontend

  • React.js (cuando el proyecto lo requiere)
  • HTML5
  • JavaScript Vanilla
  • CSS / Tailwind (opcional)

3D / XR

  • A-Frame
  • Three.js
  • 8th Wall XR Extras
  • GLTF / GLB models

Creación de componentes personalizados (A-Frame)

Un componente de A-Frame encapsula la lógica necesaria para una funcionalidad concreta.

En 8th Wall, estos componentes se agregan dentro del archivo app.js y se importan desde la carpeta /files.

Estructura típica de un componente

export const audioComponent = {
init() {
// Código ejecutado una sola vez cuando inicia el componente
},

tick() {
// Código ejecutado en cada frame/render del componente
},
};

Registro del componente

import { audioComponent } from './audio'
AFRAME.registerComponent('audio', audioComponent)

**Uso del componente**

En el HTML / A-Frame:
`<a-entity audio>`

<a-entity audio></a-entity>

Creación de elementos dinámicos

Existen dos formas de agregar elementos en tiempo real:


1. Creación por HTML

Simplemente se coloca en el body.html:

  • <a-entity>
  • <a-image>
  • <a-gltf-model>
  • <a-text>
  • Etc.

8th Wall interpreta este HTML automáticamente.


2. Creación por JavaScript

Se recomienda cuando:

  • El objeto depende de interacción del usuario
  • Los elementos requieren coordenadas dinámicas
  • Se desean spawnear entidades bajo condiciones específicas
  • Se agregan o remueven elementos durante runtime

Ejemplo real (creación de modelo)

createHen() {
const hen = document.createElement('a-entity')
hen.setAttribute('id', 'hen')
hen.setAttribute('gltf-model', '#hen')
hen.setAttribute('xrextras-pinch-scale', '')
hen.setAttribute('animation-mixer', 'clip: idle; loop: repeat')
hen.setAttribute('xrextras-two-finger-rotate', '')
hen.setAttribute('xrextras-hold-drag', '')
hen.setAttribute('environment-map', 'preset: #esplanade')
hen.classList.add('cantap')
hen.setAttribute('material', 'envMap:#studio; metalness:4.0; roughness:0.0')
hen.setAttribute('scale', '12 12 12')
hen.setAttribute('rotation', '0 -23.215 0')
hen.setAttribute('shadow', 'receive: false')
hen.setAttribute('reflections', 'type: realtime')
hen.setAttribute('position', `${this.lastPoint.x} ${this.lastPoint.y} ${this.lastPoint.z}`)

return hen
}

Una vez creado, debe agregarse al DOM:

document.querySelector('a-scene').appendChild(hen)

A-Frame + 8th Wall

Todas las características de A-Frame son 100% compatibles con 8th Wall.

Por ello, es fundamental conocer o consultar la documentación oficial:

👉 Documentación completa de A-Frame
https://aframe.io/docs


Targets en 8th Wall

8th Wall soporta los siguientes tipos de targets para experiencias Image Targets:

Tipos de Targets soportados

  • Planos (Flat Images)
  • Cilíndricos (Curved Surfaces)
  • Cónicos (Packaging tipo botella)

Esto permite experiencias webAR en:

  • Latas
  • Botellas
  • Empaques
  • Posters
  • Empaques con formas no planas

Proceso para cargar Targets en 8th Wall

  1. Ir a la sección:
    Project → Image Targets

  2. Subir los archivos del target:

    • formato recomendado: PNG/JPG de alta calidad
    • contraste fuerte
    • sin transparencias
  3. Esperar el procesamiento automático de 8th Wall.

  4. Copiar el nombre del target generado.

  5. Dentro del body.html, colocar el target así:

<a-entity mindar-image-target="targetIndex: 0"></a-entity>

ó en 8th Wall:

<a-entity xrimage="target: nombre-del-target"></a-entity>
  1. Se pueden usar múltiples targets en el mismo proyecto.

Buenas prácticas para Targets

  • Evitar imágenes con:

    • áreas grandes blancas
    • repetición de patrones
    • reflejos
    • baja resolución
  • Usar imágenes con:

    • textura
    • bordes definidos
    • alto contraste
    • características únicas

Peso ideal:

  • Entre 300 KB y 800 KB por imagen.

Tamaño recomendado:

  • 1000–2000 px de ancho.

Limitaciones de Assets en 8th Wall

Modelos 3D (GLB/GLTF)

  • Peso recomendado:
    • < 8 MB por archivo
    • Ideal: 2–6 MB por modelo
  • Los proyectos deben cargar rápido en móviles, por lo que:
    • reducir polígonos
    • texturas comprimidas
    • usar Draco compression cuando sea posible

Texturas

  • Tamaño máximo recomendable: 2048px
  • Usar formato .jpg para difusas y .png solo cuando haya transparencia
  • Ideal: 512px – 1024px para objetos pequeños

Audio

  • Usar .mp3 o .ogg
  • Evitar audios > 500 KB

Iluminación

  • Evitar demasiadas luces en A-Frame (costoso en GPU móvil)

HDRI / Environment Maps

  • Reducir tamaño
  • Comprimir
  • Usar versiones LDR siempre que sea posible

Scripts y lógica

  • Evitar loops innecesarios en tick()
  • Mantener componentes ligeros
  • Liberar memoria al remover elementos

Recomendaciones adicionales (Inmersys)

1. Capa 2D previa

Siempre incluir:

  • Logo
  • Botón de comenzar
  • Pantalla de instrucciones
  • Aviso de privacidad

2. Orden de carga

  1. Cargar XRExtras
  2. Cargar A-Frame
  3. Cargar modelos
  4. Mostrar UI 2D
  5. Entrar a AR

3. Depuración

  • Usar la consola de 8th Wall (muy útil)
  • Habilitar XRExtras error logger

Seguimiento de Raycast para mover un elemento 3D (cursor sobre plano)

El siguiente código permite detectar intersecciones mediante raycasting y mover un objeto 3D (un cursor) sobre un plano detectado por 8th Wall/A-Frame.
Incluye un sistema de suavizado para evitar saltos bruscos.

stick() {
if (this.hasFinished) return
if (!this.raycaster) return

const { intersections } = this.raycaster
if (intersections.length === 0) return

let point = null
for (let i = 0; i < intersections.length; i++) {
const { el } = intersections[i].object
if (el && el.id === 'ground') {
point = intersections[i].point
}
}

if (!point) return

if (!this.lastPoint || !point.equals(this.lastPoint)) {
this.lastPoint = point
const cursorObj = this.cursor.object3D

// Movimiento suave
if (!this.smoothPos) {
this.smoothPos = point.clone()
}

const factor = 0.8
this.smoothPos.lerp(point, factor)
cursorObj.position.copy(this.smoothPos)
}
}

Rotación automática hacia la cámara (solo eje Y)

Este componente de A-Frame hace que un objeto 3D siempre mire hacia la cámara, únicamente sobre el eje Y, manteniendo orientación vertical estable.

AFRAME.registerComponent("scope-camera", {
init() {
this.videoObj = this.el.object3D;
},

tick() {
if (!this.videoObj) return;

const cameraEl = this.el.sceneEl.camera.el;
const camObject = cameraEl.object3D;

const dx = camObject.position.x - this.videoObj.position.x;
const dz = camObject.position.z - this.videoObj.position.z;
const angleY = Math.atan2(dx, dz);

this.videoObj.rotation.set(0, angleY, 0);
},
});

Crear un tracker de imagen (Image Target)

Este código genera dinámicamente un image target nombrado, usando el componente xrextras-named-image-target.



createImageTracker({ id, name }) {
const tracker = document.createElement('xrextras-named-image-target')
tracker.setAttribute('id', id)
tracker.setAttribute('name', name)
tracker.setAttribute('button-nav', '')
return tracker
}

Crear múltiples trackers (customizable)

Permite crear varios trackers con diferentes nombres o configuraciones.
Funciona igual que el anterior, pero se usa dentro de un generador mayor que decide cuántos crear.



createImageTracker({ id, name }) {
const tracker = document.createElement('xrextras-named-image-target')
tracker.setAttribute('id', id)
tracker.setAttribute('name', name)
tracker.setAttribute('button-nav', '')
return tracker
}

Agregar elementos dentro del tracker

Cuando el marcador (target) es detectado, se agregan elementos hijos como modelos 3D, grupos de interacción, videos, etc.

tracker.appendChild(group);
tracker.appendChild(video);

Recursos

A-Frame

https://aframe.io/docs/

8th Wall – Image Targets

https://www.8thwall.com/docs/web/#xrimage

8th Wall – XR Extras

https://www.8thwall.com/docs/web/#xrextras

Three.js (matemáticas, rotaciones, raycasting base)

https://threejs.org/docs/

GLTF Viewer (para probar modelos antes de subirlos)

https://gltf.report/

Interpolación y suavizado en Three.js (lerp)

https://threejs.org/docs/#api/en/math/Vector3.lerp


Documentación esencial